#include "interface.h"

_GenericObj *objptr(_GenericObj *ptr, int size)
{
	if(size==sizeof(void *))
	{
		ptr = *((_GenericObj **)ptr);
	}

	return ptr;
}

void *REF_OBJPTR(_GenericObj *ptr)
{
//TODO sincronizar
	if(ptr) ++ptr->refCnt;
	return ptr;
}

void *REF_PTR(_GenericObj *ptr, int size)
{
	if(!ptr) return 0;

	ptr = objptr(ptr,size);
	return REF_OBJPTR(ptr);
}

void DESTROY_OBJPTR(_GenericObj *ptr)
{
//TODO sincronizar
	if(!ptr) return;

//nao permite deletar o objeto caso nao seja o proprio
	if(ptr != ptr->self) {return;}

//permite deletar o objeto caso nao seja o proprio
//	ptr = ptr->self;

	if(ptr->refCnt) --ptr->refCnt;
	else ptr->interface->DESTROY(ptr);
}

void DESTROY_PTR(_GenericObj *ptr, int size)
{
	if(!ptr) return;

	ptr = objptr(ptr, size);
	DESTROY_OBJPTR(ptr);
}


typedef struct
{
	const char *fileName;
	const char *funcName;
	void **pointers;
	int pointersSz;
	int pointersMaxSz;
} pointersList;

pointersList *list = 0;
int listSz = 0;
int listMaxSz = 0;

void *addLocal(const char *fileName, const char *funcName, void *ptr)
{
//	if( (listSz==0) || strcmp(list[listSz-1].fileName,fileName) ||
//	    strcmp(list[listSz-1].funcName,funcName) )
	if( (listSz==0) || (list[listSz-1].fileName!=fileName) ||
	    (list[listSz-1].funcName!=funcName) )
	{
		if(listSz==listMaxSz)
		{
			listMaxSz += 10;
			list = realloc(list,sizeof(pointersList)*listMaxSz);
		}
		list[listSz].fileName = fileName;
		list[listSz].funcName = funcName;
		list[listSz].pointersSz = 0;
		listSz++;
	}
	else
	{
		int i;
		for(i=0; i<list[listSz-1].pointersSz; i++)
		{
			if(list[listSz-1].pointers[i] == ptr) return ptr;
		}
	}

	if(list[listSz-1].pointersSz==list[listSz-1].pointersMaxSz)
	{
		list[listSz-1].pointersMaxSz += 10;
		list[listSz-1].pointers = realloc(list[listSz-1].pointers,sizeof(void *)*list[listSz-1].pointersMaxSz);
	}
	list[listSz-1].pointers[list[listSz-1].pointersSz] = ptr;
	list[listSz-1].pointersSz++;

	return ptr;
}

void initLocal(const char *fileName, const char *funcName, _GenericInterface *i, void *var, const char *varName)
{
printf("LOCAL %s %x ",varName,var);
	if(varName[0]=='*')
	{
printf("CREATE\n");
		void *tmp = i->create();
		*((void **) var) = tmp;
		var = tmp;
	}
	else
	{
printf("INIT\n");
		i->INIT(var);
	}
	addLocal(fileName, funcName, var);
}

void delLocals(const char *fileName, const char *funcName)
{
//	if(listSz && !strcmp(list[listSz-1].fileName,fileName) && !strcmp(list[listSz-1].funcName,funcName))
	if(listSz && (list[listSz-1].fileName==fileName) && (list[listSz-1].funcName==funcName))
	{
		--listSz;
		int i;
		for(i=0;i<list[listSz].pointersSz;i++)
		{
			_GenericObj *obj = (_GenericObj *)list[listSz].pointers[i];
			DESTROY_OBJPTR(obj);
		}
	}
}


#include <sys/types.h>
#include <fcntl.h>
char *exeName="";
void setupLibIF(int argc, char **argv)
{
	exeName = argv[0];
}

typedef struct
{
	char *name;
	void *ptr;
} PTRCache;

PTRCache *ifs = 0;
int ifsSz = 0;
int ifsSzMax = 0;

void addCache(const char *name, void *ptr)
{
	if(ifsSz==ifsSzMax)
	{
		ifsSzMax += 10;
		ifs = realloc(ifs,sizeof(PTRCache)*ifsSzMax);
	}
	ifs[ifsSz].name = strdup(name);
	ifs[ifsSz].ptr = ptr;
	++ifsSz;
}

_GenericInterface *findIFFile(const char *name)
{
#ifndef O_BINARY
#define O_BINARY 0
#endif

#define BUFSZ 4*1024
#define ENTRYSZ sizeof(IFEntry)
    char buf[BUFSZ+ENTRYSZ];

#define FOUND_BASE 1
#define FOUND_NAME 2
	int found=0;
	void *basePtr=0;
	void *fnPtr=0;

	int i;
	for(i=0; i<ifsSz; i++)
	{
		if( !strcmp(ifs[i].name,name) )
		{
			return ifs[i].ptr;
		}

		if( !strcmp(ifs[i].name,"base") )
		{
			found = FOUND_BASE;
			basePtr = ifs[i].ptr;
		}
	}

	memset(buf, 0, BUFSZ+ENTRYSZ);

	int fd = open(exeName, O_RDONLY | O_BINARY);
	int len = read(fd, buf, BUFSZ+ENTRYSZ);
    if(len<(int)ENTRYSZ) return 0;

    while(1)
    {
        int verifLen = (len>BUFSZ)?BUFSZ:len;
        for(i=0; i<verifLen; i++)
        {
            if(!memcmp(IFTAG4,&buf[i],4))
            {
                if(!memcmp(name, &buf[i+4], strlen(name)+1))
                {
                    memcpy(&fnPtr, &buf[i+ENTRYSZ-sizeof(void *)], sizeof(void *));
                    found |= FOUND_NAME;
                }
                else if(!(found & FOUND_BASE) && !memcmp("base", &buf[i+4], 4+1))
                {
                    memcpy(&basePtr, &buf[i+ENTRYSZ-sizeof(void *)], sizeof(void *));
                    found |= FOUND_BASE;
                    addCache("base",basePtr);
                }
                if(found == (FOUND_BASE|FOUND_NAME)) break;
            }
        }
        if(found == (FOUND_BASE|FOUND_NAME)) break;

        len -= BUFSZ;
        if(len<(int)ENTRYSZ) break;

        for(i=0;i<ENTRYSZ;i++) buf[i] = buf[i+BUFSZ];
        int sz = read(fd,buf+ENTRYSZ,BUFSZ);
        if(sz>0) len+=sz;
    }

	close(fd);

	if(found!=(FOUND_BASE|FOUND_NAME)) return 0;

	unsigned long v1 = (unsigned long)basePtr;
	unsigned long p1 = (unsigned long)findIFFile;
	unsigned long v2 = (unsigned long)fnPtr;
	unsigned long p2 = (p1-v1)+v2;

	_GenericInterface* (*fptr)() = (void *)p2;

	_GenericInterface *result = fptr();

	addCache(name,result);
	return result;
}
IFEntry base={IFTAG4"base", findIFFile};

#include "dl/dl.h"
LibHandler **handlers;
int handlersSize = 0;

_GenericInterface *findIFLibHandlers(char *name)
{
	LibHandler currProc = getCurrentProccesHandler();
	_GenericInterface* (*fptr)();
	char newName[256];

	strcpy(newName,name);
	strcat(newName,"I");

	if(currProc)
	{
		fptr = (_GenericInterface* (*)()) LibFind(currProc, newName);
		if(fptr) return fptr();
	}

	return 0;
}

_GenericInterface *findIFEntry(_GenericInterface* (*fptr)(), char *name)
{
	_GenericInterface *result = 0;

	if(fptr) result = fptr();
	else
	{
		result = findIFLibHandlers(name);
		if(!result) result = findIFFile(name);
	}

	return result;
}



unsigned int _addressHash(void *obj)
{
	return (unsigned int)obj;
}

int _addressCompare(void *obj1, void *obj2)
{
	if(obj1 == obj2) return 0;

	return obj1-obj2;
}

static unsigned int poorHash(unsigned char *ptr, int size)
{
	int i;
	unsigned int poorHash = 0;
	int shift = 0;

	for(i=0; i<size; i++)
	{
		poorHash += *ptr << shift;
		if(shift==24) shift = 0;
		else shift += 8;
		++ptr;
	}

	return poorHash;
}

unsigned int _binaryHash(_GenericObj *obj)
{
	int size = ((_GenericObj *)obj)->interface->_SIZEOF();

	return poorHash((unsigned char *)obj, size);
}

int _binaryCompare(_GenericObj *obj1, _GenericObj *obj2)
{
	if(obj1 == obj2) return 0;

	int size1 = ((_GenericObj *)obj1)->interface->_SIZEOF();
	int size2 = ((_GenericObj *)obj2)->interface->_SIZEOF();
	if(size1!=size2) return size1-size2;

	return memcmp(obj1,obj2,size1);
}

unsigned int _pcharHash(char *ptr)
{
	return poorHash((unsigned char *)ptr,strlen(ptr));
}

int _pcharCompare(char *ptr1, char *ptr2)
{
	if(ptr1 == ptr2) return 0;

	return strcmp(ptr1, ptr2);
}

void copyFNEntries(FNEntry *dst, _GenericInterface *srcIF)
{
	if(!srcIF) return;

	FNEntry *src = srcIF->fns;
	int i=0;
	while(src[i].fname)
	{
		dst[i].fname = src[i].fname;
		dst[i].fptr = src[i].fptr;
		++i;
	}
}

void setFNEntry(FNEntry *dst, char *name, void *ptr)
{
	int i=0;
	while(dst[i].fname)
	{
		if(!strcmp(dst[i].fname, name))
		{
			dst[i].fptr=ptr;
			return;
		}
		++i;
	}

	dst[i].fname = name;
	dst[i].fptr = ptr;
}

void *findFNPtr(_GenericInterface *gi, char *fname)
{
	int i=0;
	while(gi->fns[i].fname)
	{
		if(!strcmp(gi->fns[i].fname, fname)) return gi->fns[i].fptr;
		++i;
	}
	return 0;
}
